"""
余弦相似度
"""
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from keras.losses import CosineSimilarity


def get_data():
    """

    :return:
    """
    # a = np.array([[1,1],[1,2]])
    # b = np.array([[2,1],[2,2],[2,3]])
    a = np.array([[0, 1], [1, 1]])
    b = np.array([[1, 0], [1, 1]])

    return a.astype(np.float32), b.astype(np.float32)


def compute_cos_similar(a, b, axis=1):
    """
    A * B = ||A|| * ||B|| * cosin(theta)
    :param a:
    :param b:
    :param axis:默认axis = 1： 按行相加；axis = 0： 按列相加；axis = None：所有元素和
    :return:
    """
    molecular = np.dot(a, b.T)

    # 矩阵二范数
    # np.multiply：对应元素相乘
    # np.dot: 矩阵乘法
    # *: 数组：对应位置相乘；矩阵：矩阵乘法
    a_norm = np.sqrt(np.multiply(a, a).sum(axis=axis))
    a_norm = a_norm.reshape(-1, 1)
    # 矩阵按l2范数scale
    l2_norm_a = a / a_norm

    # 矩阵二范数
    b_norm = np.sqrt(np.multiply(b, b).sum(axis=axis))
    b_norm = b_norm.reshape(-1, 1)
    # 矩阵按l2范数scale
    l2_norm_b = b / b_norm

    denominator = np.dot(a_norm, b_norm.T)

    # 按keras.losses.CosineSimilarity算法计算
    keras_cos = l2_norm_a * l2_norm_b
    keras_cos_idx = np.flatnonzero(keras_cos)
    keras_cos_flatten = keras_cos.flatten()
    keras_cos_rs = keras_cos_flatten[keras_cos_idx].mean()

    return molecular / denominator, -keras_cos_rs


def run():
    """
    主程序
    :return:
    """
    a, b = get_data()

    # 自己算
    my_cos1, my_cos2 = compute_cos_similar(a=a, b=b)

    # sklearn
    sklearn_cos = cosine_similarity(a, b)

    # keras
    keras_cos = CosineSimilarity(axis=1)
    keras_cos_rs = keras_cos(a, b)

    print('my cosin: {}; {}\nsklearn cos: {}\nkeras cos: {}'.
          format(my_cos1, my_cos2, sklearn_cos, keras_cos_rs))


if __name__ == '__main__':
    run()
